//
/**
 * Filename: fig10-28_system2.c
 * Author:   csbonkers
 * Contact:  forever65535@qq.com
 * Date:     2021/6/20
 * Description:
 *    进行信号处理的system函数
 */

#include <apue.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>

// with appropriate signal handler
int c_system2(const char *cmdstring) {
  if (cmdstring == NULL) {
    return 1;
  }
  struct sigaction ignore, saveintr, savequit;
  ignore.sa_handler = SIG_IGN;// ignore SIGINT and SIGQUIT
  sigemptyset(&ignore.sa_mask);
  ignore.sa_flags = 0;
  if (sigaction(SIGINT, &ignore, &saveintr) < 0) {
    return -1;
  }
  if (sigaction(SIGQUIT, &ignore, &savequit) < 0) {
    return -1;
  }

  sigset_t chldmask, savemask;
  sigemptyset(&chldmask); /*now block SIGCHLD*/
  sigaddset(&chldmask, SIGCHLD);
  if (sigprocmask(SIG_BLOCK, &chldmask, &savemask) < 0) {
    return -1;
  }
  pid_t pid;
  int status;
  if ((pid = fork()) < 0) {
    return -1;// probably out of process
  } else if (pid == 0) {
    // child
    //restore previous signal action & reset signal mask
    sigaction(SIGINT, &saveintr, NULL);
    sigaction(SIGQUIT, &savequit, NULL);
    execl("/bin/sh", "sh", "-c", cmdstring, (char *) 0);
    _exit(127);// exec error
  } else {
    while (waitpid(pid, &status, 0) < 0) {
      if (errno != EINTR) {
        status = -1;
        break;
      }
    }
  }
  /*restore previous signal action & reset signal mask*/
  if (sigaction(SIGINT, &saveintr, NULL) < 0) {
    return -1;
  }
  if (sigaction(SIGQUIT, &savequit, NULL) < 0) {
    return -1;
  }
  if (sigprocmask(SIG_SETMASK, &savemask, NULL) < 0) {
    return -1;
  }
  return status;
}

static void sig_int(int signo){
  printf("caught SIGINT\n");
}
static void sig_chld(int signo){
  printf("caught SIGCHLD\n");
}

int main(){
  if(c_signal(SIGINT, sig_int) == SIG_ERR){
    printf("signal(SIGINT) error");
  }
  if(c_signal(SIGCHLD, sig_chld) == SIG_ERR){
    printf("signal(SIGCHLD) error");
  }
  if(c_system2("ed") < 0){
    err_sys("system() error");
  }
  return 0;
}
